/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package phd.dupenois.changeidentifiers.optimisation.singlepattern;

import java.util.Random;
import java.util.Vector;
import phd.dupenois.changeidentifiers.ChangeIdentifierHolder;
import phd.dupenois.changeidentifiers.ChangeIdentifierSet;
import phd.dupenois.changeidentifiers.dotpatterns.DotPattern;
import phd.dupenois.changeidentifiers.engine.TerminatingRunEngine;
import phd.dupenois.changeidentifiers.engine.testing.Test;
import phd.dupenois.changeidentifiers.exceptions.ChangeIdentifierLoadException;
import phd.dupenois.changeidentifiers.exceptions.ChangeIdentifierSetNotValidException;
import phd.dupenois.changeidentifiers.footprints.algorithms.FootprintAlgorithm;
import phd.dupenois.es.nplusn.NplusNEngine;
import phd.dupenois.es.nplusn.ObjectiveResult;

/**
 *
 * @author Max
 */
public class SinglePattern extends NplusNEngine<ChangeIdentifierSolution> {

//    private static String[][] identifiers = new String[][]{
//       new String[]{
//           "C:\\Users\\Max\\Development\\Java\\ChangeIdentifierProjects\\ChangeIdentifiersProjects\\CollectedChangeIdentifiers\\dist\\CollectedChangeIdentifiers.jar",
//           "phd.dupenois.changeidentifiers.areaboundingbox.AreaOfBoundingBox"
//       },
//       new String[]{
//           "C:\\Users\\Max\\Development\\Java\\ChangeIdentifierProjects\\ChangeIdentifiersProjects\\CollectedChangeIdentifiers\\dist\\CollectedChangeIdentifiers.jar",
//           "phd.dupenois.changeidentifiers.areaboundingbox.AreaSymmetricDifferenceBoundingBox"
//       },
//       new String[]{
//           "C:\\Users\\Max\\Development\\Java\\ChangeIdentifierProjects\\ChangeIdentifiersProjects\\CollectedChangeIdentifiers\\dist\\CollectedChangeIdentifiers.jar",
//           "phd.dupenois.changeidentifiers.centroidmovement.ChangeInCentroidScaled"
//       }
//       ,
//       new String[]{
//           "C:\\Users\\Max\\Development\\Java\\ChangeIdentifierProjects\\ChangeIdentifiersProjects\\CollectedChangeIdentifiers\\dist\\CollectedChangeIdentifiers.jar",
//           "phd.dupenois.changeidentifiers.timestepcount.TimeStepCount"
//       }
//   };
    private static String[][] identifiers = new String[][]{
        new String[]{
            "/secamfs/userspace/phd/mpd209/NetBeansProjects/ChangeIdentifierProjects/ChangeIdentifiersProjects/CollectedChangeIdentifiers/dist/CollectedChangeIdentifiers.jar",
            "phd.dupenois.changeidentifiers.areaboundingbox.AreaOfBoundingBox"
        },
        new String[]{
            "/secamfs/userspace/phd/mpd209/NetBeansProjects/ChangeIdentifierProjects/ChangeIdentifiersProjects/CollectedChangeIdentifiers/dist/CollectedChangeIdentifiers.jar",
            "phd.dupenois.changeidentifiers.areaboundingbox.AreaSymmetricDifferenceBoundingBox"
        },
        new String[]{
            "/secamfs/userspace/phd/mpd209/NetBeansProjects/ChangeIdentifierProjects/ChangeIdentifiersProjects/CollectedChangeIdentifiers/dist/CollectedChangeIdentifiers.jar",
            "phd.dupenois.changeidentifiers.centroidmovement.ChangeInCentroidScaled"
        },
        new String[]{
            "/secamfs/userspace/phd/mpd209/NetBeansProjects/ChangeIdentifierProjects/ChangeIdentifiersProjects/CollectedChangeIdentifiers/dist/CollectedChangeIdentifiers.jar",
            "phd.dupenois.changeidentifiers.timestepcount.TimeStepCount"
        }
    };
//    private double minimumUnit = 0.1;
    private double newIdentifiersChance = 0.2;
//    private double newParentChance = 0.5;
    private int maxIdentifiers = 1;
    private TerminatingRunEngine ciEngine;
    private int version = 0;
    private Random randomNewIdentifiers;
    private Random randomNumberIdentifiers;
    private Random randomIdentifier;
    private Random randomPriority;
    private Random randomThreshold;
    private Random randomTotalThreshold;
    private Random randomRedrawOnFail;
    private Random randomMaxFails;
    private Random randomMultiplier;

    private Vector<DotPattern> patterns;
    private FootprintAlgorithm algorithm;
    private Application application;
    public SinglePattern(TerminatingRunEngine ciEngine, int maxGeneration,
                        int population, Application application,
                        Vector<DotPattern> patterns, FootprintAlgorithm algorithm) {
        super();
        addListener(application);
        setMaximumGenerations(maxGeneration);
        setPopulation(population);
        this.patterns = patterns;
        this.algorithm = algorithm;
        this.application = application;
        this.ciEngine = ciEngine;
        this.randomNumberIdentifiers = new Random(System.currentTimeMillis());
        this.randomIdentifier = new Random(System.currentTimeMillis());
        this.randomPriority = new Random(System.currentTimeMillis());
        this.randomThreshold = new Random(System.currentTimeMillis());
        this.randomTotalThreshold = new Random(System.currentTimeMillis());
        this.randomRedrawOnFail = new Random(System.currentTimeMillis());
        this.randomMaxFails = new Random(System.currentTimeMillis());
        this.randomMultiplier = new Random(System.currentTimeMillis());
        this.randomNewIdentifiers = new Random(System.currentTimeMillis());
    }

    private ChangeIdentifierHolder randomIdentifier(int numberOfIdentifiers){
        int index = randomIdentifier.nextInt(identifiers.length);
        String[] details = identifiers[index];
        int priority = randomPriority.nextInt(numberOfIdentifiers);
        double threshold = randomThreshold.nextDouble() * 100;
        double multiplier = randomMultiplier.nextDouble();
        boolean redrawOnFail = randomRedrawOnFail.nextBoolean();
        try {
            return new ChangeIdentifierHolder(details[0], details[1], priority, threshold, redrawOnFail, multiplier);
        } catch (ChangeIdentifierLoadException ex) {
            exceptionThrown(ex);
            System.exit(1);
            return null;
        }
    }

    public ChangeIdentifierSolution generateRandomSolution(){
        ChangeIdentifierSet changeIdentifierSet = new ChangeIdentifierSet();
        int numberOfIdentifiers = randomNumberIdentifiers.nextInt(maxIdentifiers) + 1;
        ChangeIdentifierHolder holder;
        for (int i = 0; i < numberOfIdentifiers; i++) {
            holder = randomIdentifier(numberOfIdentifiers);
            changeIdentifierSet.add(holder);
        }
        changeIdentifierSet.setConcurrent(false);
        double threshold = randomTotalThreshold.nextDouble() * 100;
        int maxFails = randomMaxFails.nextInt(numberOfIdentifiers) + 1;
        changeIdentifierSet.setMaxFails(maxFails);
        changeIdentifierSet.setTotalThreshold(threshold);
        version = 0;
        changeIdentifierSet.setVersion(getVersionString());
        changeIdentifierSet.setName("random-set-created-" + System.currentTimeMillis());
        changeIdentifierSet.setDescription("Random set created at " + System.currentTimeMillis());
        
        return new ChangeIdentifierSolution(changeIdentifierSet);
    }

    private String getVersionString() {
        int length = (getMaximumGenerations() + "").length();
        StringBuffer current = new StringBuffer(""+version);
        while (current.length() < length) {
            current = current.insert(0, "0");
        }
        return current.insert(0, "1.").toString();
    }




    public ObjectiveResult[] computeResults(ChangeIdentifierSolution solution) {
        double timeTaken = -1;
        double error = -1;
        String title = "Optimisation Running Test";
        title += "[" + getGeneration() + "]";
        try {
           if(!solution.getChangeIdentifierSet().isInitialised())
               solution.getChangeIdentifierSet().initialise(application);
        } catch (ChangeIdentifierSetNotValidException ex) {
            exceptionThrown(ex);
            System.exit(1);
        }
        Test test = ciEngine.runEngine(algorithm, solution.getChangeIdentifierSet(),
                patterns.toArray(new DotPattern[patterns.size()]),
                title, "Run by the optimisation engine", 0);

        timeTaken = test.getTimeTaken();


        error = SymmetricAreaDifferenceError.getAverageError(test);
        ObjectiveResult[] result = new ObjectiveResult[]{
            solution.createObjectiveResult(ChangeIdentifierSolution.TIME_TAKEN_LABEL, timeTaken),
            solution.createObjectiveResult(ChangeIdentifierSolution.SYMMETRIC_ERROR_LABEL, error)
        };

//        double[] result  = new double[]{timeTaken, error};

//        System.out.println("Set "+set.getName()+" "+set.getVersion()+" Took "+
//                        result[0]+"ms for an error of "+result[1]);
//        System.out.println("With Change Identifiers redrew "+
//                test.getRedrawCount()+"/"+
//                test.getRedrawCountWithoutChangeIdentifiers());
//        System.out.println("-----------");

//        System.out.println("Set: "+set.getName()+" "+set.getVersion()+" took "+timeTaken+"ms at total error: "+error);

        return result;
    }


    public ChangeIdentifierSolution mutateSolution(ChangeIdentifierSolution originalSol) {
        ChangeIdentifierSet original = originalSol.getChangeIdentifierSet();
        //Chance to completely redo identifiers, otherwise just modify them
        boolean newIdentifiers = randomNewIdentifiers.nextDouble() < newIdentifiersChance;
//        if(newIdentifiers) System.out.println("Creating new random identifier");
        if (newIdentifiers) {
            return generateRandomSolution();
        } else {

            ChangeIdentifierSet mutatedChangeIdentifierSet = new ChangeIdentifierSet();
            mutatedChangeIdentifierSet.setTotalThreshold(addNoise(original.getTotalThreshold(),
                    100d, new double[]{0}));
            mutatedChangeIdentifierSet.setMaxFails(addNoise(original.getMaxFails(),
                    10, new int[]{1, original.getChangeIdentifierHolders().length}));

            version++;
//            System.out.println("Mutant Version "+version+" == "+getVersionString());

            mutatedChangeIdentifierSet.setName(original.getName());
            mutatedChangeIdentifierSet.setDescription(original.getDescription());
            mutatedChangeIdentifierSet.setVersion(getVersionString());
            for (ChangeIdentifierHolder holder : original.getChangeIdentifierHolders()) {
                mutatedChangeIdentifierSet.add(mutateChangeIdentifier(holder));
            }
            return new ChangeIdentifierSolution(mutatedChangeIdentifierSet);
        }

    }

    private ChangeIdentifierHolder mutateChangeIdentifier(ChangeIdentifierHolder original){
        int priority = addNoise(original.getPriority(), 10, new int[]{0});
        double threshold = addNoise(original.getThreshold(), 100d, new double[]{0});
        double multiplier = addNoise(original.getMultiplier(), 1d, new double[]{0});
        boolean redrawOnFail = randomRedrawOnFail.nextBoolean();
        try {
            return new ChangeIdentifierHolder(original.getLocation(), original.getClassname(),
                    priority, threshold, redrawOnFail, multiplier);
        } catch (ChangeIdentifierLoadException ex) {
            exceptionThrown(ex);
            System.exit(1);
            return null;
        }
    }




    public void exceptionThrown(Exception ex) {
        int lineNo = ex.getStackTrace()[0].getLineNumber();
        System.out.println("[ES EXCEPTION-"+lineNo+"] " + ex.getClass().getSimpleName() + " :: " + ex.getMessage());
    }



   
}
